use std::path::PathBuf;
use std::process::Stdio;
use tokio::process::Command;
use tracing::{info, error, debug};
use anyhow::{Result, Context};

pub struct ScrcpyServer {
    device_id: String,
    scid: String,
    version: String,
    port: u16,
}

impl ScrcpyServer {
    pub fn new(device_id: String, scid: String, port: u16) -> Self {
        Self {
            device_id,
            scid,
            version: "2.3".to_string(), // Default scrcpy version
            port,
        }
    }

    /// Push scrcpy server to device
    pub async fn push_server(&self, server_path: &PathBuf) -> Result<()> {
        info!("Pushing scrcpy server to device {}", self.device_id);
        
        let output = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "push",
                &server_path.to_string_lossy(),
                "/data/local/tmp/scrcpy-server.jar"
            ])
            .output()
            .await
            .context("Failed to push scrcpy server")?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            error!("Failed to push server: {}", error);
            return Err(anyhow::anyhow!("Failed to push server: {}", error));
        }

        info!("Successfully pushed scrcpy server to device");
        Ok(())
    }

    /// Forward local port to device
    pub async fn forward_port(&self) -> Result<()> {
        info!("Forwarding port {} for device {}", self.port, self.device_id);
        
        let output = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "forward",
                &format!("tcp:{}", self.port),
                &format!("localabstract:scrcpy_{}", self.scid),
            ])
            .output()
            .await
            .context("Failed to forward port")?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            error!("Failed to forward port: {}", error);
            return Err(anyhow::anyhow!("Failed to forward port: {}", error));
        }

        info!("Successfully forwarded port {}", self.port);
        Ok(())
    }

    /// Reverse device port to local
    pub async fn reverse_port(&self) -> Result<()> {
        info!("Reversing port {} for device {}", self.port, self.device_id);
        
        let output = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "reverse",
                &format!("localabstract:scrcpy_{}", self.scid),
                &format!("tcp:{}", self.port),
            ])
            .output()
            .await
            .context("Failed to reverse port")?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            error!("Failed to reverse port: {}", error);
            return Err(anyhow::anyhow!("Failed to reverse port: {}", error));
        }

        info!("Successfully reversed port {}", self.port);
        Ok(())
    }

    /// Start scrcpy server on device
    pub async fn start(&self) -> Result<()> {
        info!("Starting scrcpy server on device {}", self.device_id);
        
        let mut child = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "shell",
                "CLASSPATH=/data/local/tmp/scrcpy-server.jar",
                "app_process",
                "/",
                "com.genymobile.scrcpy.Server",
                &self.version,
                &format!("scid={}", self.scid),
                "tunnel_forward=true",
                "video=false",  // Disable video for control-only mode
                "audio=false",  // Disable audio
                "control=true", // Enable control
            ])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .context("Failed to start scrcpy server")?;

        // Spawn a task to monitor the server process
        tokio::spawn(async move {
            match child.wait().await {
                Ok(status) => {
                    if status.success() {
                        info!("Scrcpy server exited normally");
                    } else {
                        error!("Scrcpy server exited with error: {}", status);
                    }
                }
                Err(e) => {
                    error!("Failed to wait for scrcpy server: {}", e);
                }
            }
        });

        info!("Scrcpy server started successfully");
        Ok(())
    }

    /// Stop scrcpy server
    pub async fn stop(&self) -> Result<()> {
        info!("Stopping scrcpy server on device {}", self.device_id);
        
        // Remove port forwarding
        let _ = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "forward",
                "--remove",
                &format!("tcp:{}", self.port),
            ])
            .output()
            .await;

        // Kill server process on device
        let _ = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "shell",
                "pkill", "-f", "scrcpy-server",
            ])
            .output()
            .await;

        info!("Scrcpy server stopped");
        Ok(())
    }

    /// Get device screen size
    pub async fn get_screen_size(&self) -> Result<(u32, u32)> {
        let output = Command::new("adb")
            .args(&[
                "-s", &self.device_id,
                "shell",
                "wm", "size",
            ])
            .output()
            .await
            .context("Failed to get screen size")?;

        if !output.status.success() {
            return Err(anyhow::anyhow!("Failed to get screen size"));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        for line in output_str.lines() {
            if line.starts_with("Physical size: ") {
                let size_str = line.trim_start_matches("Physical size: ");
                let parts: Vec<&str> = size_str.split('x').collect();
                if parts.len() == 2 {
                    let width = parts[0].parse::<u32>()
                        .context("Failed to parse width")?;
                    let height = parts[1].parse::<u32>()
                        .context("Failed to parse height")?;
                    return Ok((width, height));
                }
            }
        }

        Err(anyhow::anyhow!("Could not parse screen size"))
    }
}